<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        canvas {
            display:block;
            margin:20px auto;
            border:2px solid #ccc;
        }
    </style>
</head>
<body>
    <canvas width="800" height="500"></canvas>
    <script>
        var cas = document.querySelector('canvas');
        var ctx = cas.getContext('2d');
        //度数的封装
        function toRadian(angle){
            return angle * Math.PI / 180;
        }
        //
        function Arc(option){
            if(this === window) return new Arc(option);
            this.cas = option.cas;
            this.ctx = this.cas.getContext('2d');
            this.x = option.data[0];
            this.y = option.data[1];
            this.radius = option.data[2];
            //如果是用比率绘制数据圆形
            if(option.rateArr){
                this.rateArr = option.rateArr;
                this.colors = option.colors;
            }else {
                this.start = option.data[3];
                this.end = option.data[4];
            }
            //默认填充为楔形
            if(option.stroke){
                this.stroke = option.stroke;
                if(option.lineWidth) this.lineWidth = option.lineWidth;
            }
            else this.fill = option.fill || 'dodgerblue'; 
            //如果有写字
            if(option.txt || option.txtArr){
                //如果是单个字
                if(option.txt) this.txt = option.txt;
                //如果是字组
                if(option.txtArr) this.txtArr = option.txtArr;
                //字体样式
                if(option.font) this.font = option.font;
                //线样式
                if(option.lineStroke) this.lineStroke = option.lineStroke;
                //线超出长度
                if(option.distance) this.distance = option.distance;
                //字偏移值
                if(option.txtPadding) this.txtPadding = option.txtPadding;
            }
        }
        Arc.prototype = {constructor:Arc,
            draw: function(){
                //参数
                var cas = this.cas,
                    ctx = this.ctx,
                    x = this.x,
                    y = this.y,
                    r = this.radius;
                //如果是用比率绘制圆形
                if(this.rateArr){
                    var rateArr = this.rateArr,
                        colors = this.colors;
                    if(this.txtArr) var txtArr = this.txtArr;
                }else {
                    //单个绘制
                    var start = this.start,
                        end = this.end;
                    if(this.txt) var txt = this.txt;
                } 
                //保存样式
                ctx.save();
                ctx.beginPath();
                if(rateArr) {
                    //开始绘制多个
                    //遍历比率进行绘制
                    start = -90;    //以-90度为起点
                    for(var i = 0;i<rateArr.length;i++){
                        //计算坐标
                        end = start + rateArr[i]*360;
                        //递归执行
                        //如果要写文字
                        if(txtArr){
                            //如果有样式
                            if(this.font) var font = this.font;
                            //线样式
                            if(this.lineStroke) var lineStroke = this.lineStroke;
                            //线超出长度
                            if(this.distance) var distance = this.distance;
                            //字偏移值
                            if(this.txtPadding) var txtPadding = this.txtPadding;
                            Arc({
                                cas:cas,
                                data:[x,y,r,start,end],
                                fill:colors[i],
                                txt:txtArr[i],
                                font:font,
                                lineStroke:lineStroke,
                                txtPadding:txtPadding,
                                distance:distance
                            }).draw();
                        }else {
                            Arc({
                                cas:cas,
                                data:[x,y,r,start,end],
                                fill:colors[i],
                            }).draw();
                        }
                        start = end;
                    }

                }else {
                    //开始绘制单个
                    if(this.stroke){
                        //如果是环形
                        //样式
                        ctx.strokeStyle = this.stroke;
                        if(this.lineWidth) ctx.lineWidth = this.lineWidth;
                        ctx.moveTo(
                                x + r * Math.cos(toRadian(start)),
                                y + r * Math.sin(toRadian(start))
                            );
                        ctx.arc(x,y,r,toRadian(start),toRadian(end));
                        ctx.stroke();
                    }else {
                        //如果是楔形
                        ctx.fillStyle = this.fill;
                        ctx.moveTo(x,y);
                        ctx.arc(x,y,r,toRadian(start),toRadian(end));
                        ctx.fill();
                    }
                    if(txt){
                        //如果有文字 绘制在楔形的中间
                        //保存样式
                        ctx.save();
                        ctx.beginPath();
                        //设置文字样式
                        // console.log(this.font);
                        ctx.strokeStyle = this.lineStroke || this.fill;
                        ctx.font = this.font || '15px Consoles';
                        ctx.fillStyle = this.txtColor || this.fill;
                        //线超出楔形的距离   文字偏移距离  
                        var distance = this.distance || 20,
                            txtPadding = this.txtPadding || 8,
                        //得到文字底线长度    当前角大小
                            txtLine = ctx.measureText(txt).width + 2 * txtPadding,
                            angle = start + (end - start) * .5;
                        //得到交点坐标
                            x1 = x + (r + distance) * Math.cos( toRadian(angle) ),
                            y1 = y + (r + distance) * Math.sin( toRadian(angle) );
                        //移动到中点
                        ctx.moveTo(x,y);
                        //对txtLine方向判定
                        if( angle > 90 && angle < 270) txtLine = - txtLine;
                        //画线
                        ctx.lineTo( x1,y1 );
                        ctx.lineTo( x1 + txtLine , y1 );
                        //写字
                        //字的对其方式
                        ctx.textAlign = 'center';
                        ctx.textBaseline = 'bottom';
                        //写字
                        ctx.fillText( txt,x1 + txtLine * .5,y1 - txtPadding);
                        ctx.stroke();
                        //恢复
                        ctx.restore();
                    }
                }

                //恢复样式
                ctx.restore();
                return this;
            }
        }
        //单个测试
        // Arc({
        //     cas:cas,
        //     data:[100,100,50,-90,120],
        //     txt:'一个一个一个一个',
        //     font:'20px 楷体',
        //     // lineStroke:'palevioletred',
        //     // txtPadding:20
        // }).draw();



        //对数据可视化的函数
        function ArcData(option){
            if(this === window) return new ArcData(option);
            var colors = [
                ['dodgerblue','palegreen','palevioletred','palegoldsad','yellowgreen','skyblue','seagreen','whitesmoke'],
                ['dodgerblue','palegreen','palevioletred','palegoldsad','yellowgreen','skyblue','seagreen','whitesmoke'],
                ['dodgerblue','palegreen','palevioletred','palegoldsad','yellowgreen','skyblue','seagreen','whitesmoke']
            ];
            this.cas = option.cas;
            this.ctx = this.cas.getContext('2d');
            this.x = option.arc[0];
            this.y = option.arc[1];
            this.r = option.arc[2];
            this.data = option.data;
            //如果要写字
            if(option.txtArr) {
                this.txtArr = option.txtArr;
                //字体
                if(option.font) this.font = option.font;
                //线样式
                if(option.lineStroke) this.lineStroke = option.lineStroke;
                //线超出长度
                if(option.distance) this.distance = option.distance;
                //字偏移值
                if(option.txtPadding) this.txtPadding = option.txtPadding;





            };
            this.colors = option.colors || colors[Math.floor(Math.random() * colors.length)];
        }
        ArcData.prototype = {constructor:ArcData,
            draw:function(){
                var cas = this.cas,
                    ctx = this.ctx,
                    x = this.x,
                    y = this.y,
                    r = this.r,
                    data = this.data,
                    colors = this.colors;
                if(this.txtArr) var txtArr = this.txtArr;
                //开始绘制
                //保存样式
                ctx.save();
                ctx.beginPath();
                //计算份额
                var sum = 0;
                data.forEach(function(v,i){
                    sum += v;
                });
                //百分比数组
                var rateArr = data.map(function(v,i){
                    return v / sum;
                });
                // console.log(rateArr);
                //计算画出楔形
                //绘制楔形
                //如果要写字
                if(txtArr){
                    if(this.font) var font = this.font;
                    //线样式
                    if(this.lineStroke) var lineStroke = this.lineStroke;
                    //线超出长度
                    if(this.distance) var distance = this.distance;
                    //字偏移值
                    if(this.txtPadding) var txtPadding = this.txtPadding;
                    Arc({
                        cas:cas,
                        data:[x,y,r],
                        rateArr:rateArr,
                        colors:colors,
                        txtArr:txtArr,
                        font:font,
                        lineStroke:lineStroke,
                        txtPadding:txtPadding,
                        distance:distance
                    }).draw();
                }else {
                    Arc({
                        cas:cas,
                        data:[x,y,r],
                        rateArr:rateArr,
                        colors:colors,
                    }).draw();
                }
            }
        };   
        //统一设置样式
        ArcData({
            cas:cas,
            arc:[200,200,100],
            data:[1,2,3,4,5],
            txtArr:['第一个','第二个','第三个','第四个','第五个',],
            font:"30px 楷体",
            distance:80,
        }).draw();
    </script>
</body>
</html>